<html><head>
<TITLE>Ryan's Day Black Hole Golf</TITLE>

<script src="CanvasMatrix.js" type="text/javascript"></script>
<script src="webgl-utils.js" type="text/javascript"></script>
<script src="../o3djs/base.js" type="text/javascript"></script>

<script>
o3djs.require('o3djs.math');
o3djs.require('o3djs.quaternions');
o3djs.require('o3djs.event');
</script>


<script id="shader-vs" type="x-shader/x-vertex"> 
  attribute vec3 aPos;
  attribute vec3 aNorm;
  uniform mat4 mvMatrix;
  uniform mat4 prMatrix;
  varying vec4 color;
  const vec4 dirDif = vec4(0., 0., 1., 0.);
  const vec4 dirHalf = vec4(.4034, .259, .8776, 0.);
void main(void) {
   gl_Position = prMatrix * mvMatrix * vec4(aPos, 1.);
   vec4 rotNorm = mvMatrix * vec4(aNorm, .0);
   float i = abs(dot(rotNorm, dirDif));
   color = vec4(.5*i+.2, .5*i+.2, .8*i+.2, 1.);
   i = pow( abs(dot(rotNorm, dirHalf)) , 40.);
   color += vec4(i, i, i,1.0);
}
</script> 
 
<script id="shader-fs" type="x-shader/x-fragment"> 
#ifdef GL_ES
precision highp float;
#endif
  varying vec4 color;
void main(void) {
   gl_FragColor = color;
}
</script> 
 
<script id="shader-star-vs" type="x-shader/x-vertex"> 
  attribute vec3 aPos;
  attribute vec3 oPos;
  attribute vec3 color;
  uniform mat4 mvMatrix;
  uniform mat4 prMatrix;
  varying vec2 fPos;
  varying vec3 fColor;
void main(void) {
   vec4 finPos = mvMatrix * vec4(aPos, 1.);
   gl_Position = prMatrix * (finPos + vec4(oPos, 1.));
   float m = max(abs(oPos.x), abs(oPos.y));
   fPos.x = oPos.x / m;
   fPos.y = oPos.y / m ;
   fColor = color;
}
</script> 
 
<script id="shader-star-fs" type="x-shader/x-fragment"> 
#ifdef GL_ES
precision highp float;
#endif
  varying vec2 fPos;
  varying vec3 fColor;
void main(void) {
   float b = 1.0 - sqrt(fPos.x * fPos.x  + fPos.y * fPos.y);
   if (b < 0.0) { b = 0.0; }
   gl_FragColor = vec4(fColor, b*b);
}
</script> 


<script id="shader-beam-vs" type="x-shader/x-vertex"> 
  attribute vec3 aPos;
  uniform mat4 mvMatrix;
  uniform mat4 prMatrix;
void main(void) {
   vec3 finPos;
   gl_Position = prMatrix * mvMatrix * vec4(aPos, 1.);
}
</script> 
 
<script id="shader-beam-fs" type="x-shader/x-fragment"> 
#ifdef GL_ES
precision highp float;
#endif
  varying vec2 fPos;
void main(void) {
   gl_FragColor = vec4(0.0, 0.5, 0.5, 1.0);
}
</script> 



<script src="yf23_model.js" type="text/javascript"></script>
<script type="text/javascript"> 
  enterprise_pt = pt;
  enterprise_nt = nt;
  enterprise_ind = ind;
  n_enterprise = n;
  mt_enterprise = mt;
</script>

<script type="text/javascript"> 
var gl;
function getShader ( gl, id ){
   var shaderScript = document.getElementById ( id );
   var str = "";
   var k = shaderScript.firstChild;
   while ( k ){
     if ( k.nodeType == 3 ) str += k.textContent;
     k = k.nextSibling;
   }
   var shader;
   if ( shaderScript.type == "x-shader/x-fragment" )
           shader = gl.createShader ( gl.FRAGMENT_SHADER );
   else if ( shaderScript.type == "x-shader/x-vertex" )
           shader = gl.createShader(gl.VERTEX_SHADER);
   else return null;
   gl.shaderSource(shader, str);
   gl.compileShader(shader);
   if (gl.getShaderParameter(shader, gl.COMPILE_STATUS) == 0)
      alert(gl.getShaderInfoLog(shader));
   return shader;
}

var canvas;
var g_keyDown = [];
function webGLStart() {
   canvas = document.getElementById("canvas");
   document.getElementById("status").innerHTML = "get to the green star: hold SHIFT to move with mouse or move with wasd on keyboard. left click fires attractor beam, space fires repulsor beam";
   var c_w = window.innerWidth - 25,  c_h = window.innerHeight - 75;
   canvas.width = c_w;   canvas.height = c_h;
   var err = "Your browser does not support ";
   if (!window.WebGLRenderingContext){
     alert(err + "WebGL. See http://get.webgl.org"); return;}
   try { var gl = canvas.getContext("experimental-webgl");
   } catch(e) {}
   if ( !gl ) {alert("Can't get WebGL"); return;}
   gl.viewport(0, 0, c_w, c_h);

   var prog  = gl.createProgram();
   gl.attachShader(prog, getShader( gl, "shader-vs" ));
   gl.attachShader(prog, getShader( gl, "shader-fs" ));
   gl.linkProgram(prog);
   gl.useProgram(prog);

   var posLocation = gl.getAttribLocation(prog, "aPos");
   gl.enableVertexAttribArray( posLocation );
   var posBuffer = gl.createBuffer();
   gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
   gl.bufferData(gl.ARRAY_BUFFER, enterprise_pt, gl.STATIC_DRAW);
   gl.vertexAttribPointer(posLocation, 3, gl.FLOAT, false, 0, 0);

   var normLocation = gl.getAttribLocation(prog, "aNorm");
   gl.enableVertexAttribArray( normLocation );
   var normBuffer = gl.createBuffer();
   gl.bindBuffer(gl.ARRAY_BUFFER, normBuffer);
   gl.bufferData(gl.ARRAY_BUFFER, enterprise_nt, gl.STATIC_DRAW);
   gl.vertexAttribPointer(normLocation, 3, gl.FLOAT, false, 0, 0);

   var indexBuffer = gl.createBuffer();
   gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
   gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(enterprise_ind),
     gl.STATIC_DRAW);

   var prMatrix = new CanvasMatrix4();
   var ptMatrix = new CanvasMatrix4();
   var invPtMatrix = new CanvasMatrix4();
   ptMatrix.makeIdentity();
   invPtMatrix.makeIdentity();

   gl.uniformMatrix4fv( gl.getUniformLocation(prog,"prMatrix"),
      false, new Float32Array(prMatrix.getAsArray()) );
   var mvMatrix = new CanvasMatrix4();
   var rotMat = new CanvasMatrix4();
   var mvMatLoc = gl.getUniformLocation(prog,"mvMatrix");


   var nStars = 400;
   var starOffset = new Float32Array(nStars * 18);
   var sOffset = new Float32Array(18);

   var xsPos = new Float32Array(nStars);
   var ysPos = new Float32Array(nStars);
   var zsPos = new Float32Array(nStars);
   var pull = new Float32Array(nStars);


   sOffset[0] = -1.0;
   sOffset[1] = -1.0;
   sOffset[2] = 0.0; 

   sOffset[3] = -1.0;
   sOffset[4] = 1.0;
   sOffset[5] = 0.0; 

   sOffset[6] = 1.0;
   sOffset[7] = 1.0;
   sOffset[8] = 0.0; 

   sOffset[9] = 1.0;
   sOffset[10] = -1.0;
   sOffset[11] = 0.0; 

   sOffset[12] = -1.0;
   sOffset[13] = -1.0;
   sOffset[14] = 0.0; 

   sOffset[15] = 1.0;
   sOffset[16] = 1.0;
   sOffset[17] = 0.0; 


   var starPos = new Float32Array(nStars * 18);
   var starColor = new Float32Array(nStars * 18);
   var starRange = 200;
   var goal = -400.0;
   for (i = 0; i < nStars; i++) {
     xsPos[i] = Math.random() * starRange  - starRange/2.0;
     ysPos[i] = Math.random() * starRange  - starRange/2.0;
     zsPos[i] = -Math.random() * starRange * 2 + goal/2.0;
//     pull[i] = 0.0;
     pull[i] = (Math.random() * 2.0 - 1.0)/10.0;
//     if (pull[i] < 0) { pull[i] = -1.0;} 
//     else {pull[i] = 1.0;}
   }

   //the last star is special - it's the goal
//   xsPos[nStars -1] = Math.random() * 40  - 20.0;
//   ysPos[nStars -1] = Math.random() * 40 - 20.0;
   xsPos[nStars -1] = 0.0;
   ysPos[nStars -1] = 0.0;
   zsPos[nStars -1] = goal;
   pull[nStars - 1] = 0.0;

   function updateStars() {
    for(i = 0; i < nStars - 1; i++) {
      var red = pull[i];
      if (red < 0.0)  { red = 0.0; }
      var blue = -pull[i];
      if (blue < 0.0) { blue = 0.0; }

      for (var j = 0; j < 6; j++) {
        starPos[i*18 + j*3 + 0] = xsPos[i];
        starPos[i*18 + j*3 + 1] = ysPos[i];
        starPos[i*18 + j*3 + 2] = zsPos[i];
        starColor[i*18 + j*3 + 0] = red;
        starColor[i*18 + j*3 + 1] = 0.0;
        starColor[i*18 + j*3 + 2] = blue;
      }
    }

   for (var j = 0; j < 6; j++) {
        starPos[(nStars -1)*18 + j*3 + 0] = xsPos[(nStars -1)];
        starPos[(nStars -1)*18 + j*3 + 1] = ysPos[(nStars -1)];
        starPos[(nStars -1)*18 + j*3 + 2] = zsPos[(nStars -1)];
        starColor[(nStars -1)*18 + j*3 + 0] = 0.0;
        starColor[(nStars -1)*18 + j*3 + 1] = 1.0;
        starColor[(nStars -1)*18 + j*3 + 2] = 0.0;
    }


     //update the star rotation offset
     for (i = 0; i < nStars; i++) {
       var rotM = new CanvasMatrix4(); 
       rotM.makeIdentity();
       var xD = xsPos[i];
       var yD = ysPos[i];
       var zD = zsPos[i];
       var xzD = Math.sqrt(xD * xD + zD * zD);

       var phi = Math.atan2(xD, zD);
       var theta = Math.atan(yD/xzD);

       rotM.rotate(phi, 0, 1, 0);
       rotM.rotate(-theta, 1, 0, 0);

       var s = 1.0;
       if (i == nStars - 1) {s = 1.0}
  
       for (var j = 0; j < 6; j++) {
         var x = sOffset[j*3 + 0];
         var y = sOffset[j*3 + 1];

         starOffset[i*18 + j*3 + 0] = rotM.m11 * x * s+ rotM.m12 * y * s;
         starOffset[i*18 + j*3 + 1] = rotM.m21 * x * s+ rotM.m22 * y * s;
         starOffset[i*18 + j*3 + 2] = rotM.m31 * x * s+ rotM.m32 * y * s;
       
       }
     }
   }

   //for the stars
   var starProg  = gl.createProgram();
   gl.attachShader(starProg, getShader( gl, "shader-star-vs" ));
   gl.attachShader(starProg, getShader( gl, "shader-star-fs" ));
   gl.linkProgram(starProg);
   gl.useProgram(starProg);


   var starposLocation = gl.getAttribLocation(starProg, "aPos");
   gl.enableVertexAttribArray( starposLocation );
   var starposBuffer = gl.createBuffer();
   gl.bindBuffer(gl.ARRAY_BUFFER, starposBuffer);
   gl.bufferData(gl.ARRAY_BUFFER, starPos, gl.STREAM_DRAW);
   gl.vertexAttribPointer(starposLocation, 3, gl.FLOAT, false, 0, 0);


   var starcolorLocation = gl.getAttribLocation(starProg, "color");
   gl.enableVertexAttribArray( starcolorLocation );
   var starcolorBuffer = gl.createBuffer();
   gl.bindBuffer(gl.ARRAY_BUFFER, starcolorBuffer);
   gl.bufferData(gl.ARRAY_BUFFER, starColor, gl.STREAM_DRAW);
   gl.vertexAttribPointer(starcolorLocation, 3, gl.FLOAT, false, 0, 0);



   var staroffLocation = gl.getAttribLocation(starProg, "oPos");
   gl.enableVertexAttribArray( staroffLocation );
   var staroffBuffer = gl.createBuffer();
   gl.bindBuffer(gl.ARRAY_BUFFER, staroffBuffer);
   gl.bufferData(gl.ARRAY_BUFFER, starOffset, gl.STREAM_DRAW);
   gl.vertexAttribPointer(staroffLocation, 3, gl.FLOAT, false, 0, 0);

   starIndices = new Uint16Array(nStars * 6 * 10);
   for (i = 0; i < nStars * 6 * 10; i++) {
     starIndices[i] = i;
   }

   var starIndexBuffer = gl.createBuffer();
   gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, starIndexBuffer);
   gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, starIndices, gl.STATIC_DRAW);


   var starprMatLoc = gl.getUniformLocation(starProg,"prMatrix");
   var starmvMatLoc = gl.getUniformLocation(starProg,"mvMatrix");

//beams

   var beamProg  = gl.createProgram();
   gl.attachShader(beamProg, getShader( gl, "shader-beam-vs" ));
   gl.attachShader(beamProg, getShader( gl, "shader-beam-fs" ));
   gl.linkProgram(beamProg);
   gl.useProgram(beamProg);


   var nBeam = 1000;
   beamPos = new Float32Array(3 * nBeam);

   for (i = 0; i < nBeam; i++) {
      beamPos[i*3] = 0.0;
      beamPos[i*3 + 1] = -1 + i / 1000.0;
      beamPos[i*3 + 2] = -20 -i*0.2;
   }

   var beamposLocation = gl.getAttribLocation(beamProg, "aPos");
   gl.enableVertexAttribArray( beamposLocation );
   var beamposBuffer = gl.createBuffer();
   gl.bindBuffer(gl.ARRAY_BUFFER, beamposBuffer);
   gl.bufferData(gl.ARRAY_BUFFER, beamPos, gl.STREAM_DRAW);
   gl.vertexAttribPointer(beamposLocation, 3, gl.FLOAT, false, 0, 0);

   var beamprMatLoc = gl.getUniformLocation(beamProg,"prMatrix");
   var beammvMatLoc = gl.getUniformLocation(beamProg,"mvMatrix");









//   gl.enable(gl.DEPTH_TEST);
//   gl.depthFunc(gl.LESS);

   gl.enable(gl.BLEND); //Enable alpha blending
   gl.disable(gl.DEPTH_TEST);
   gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); //Set the blend function
   gl.clearDepth(1.0);
   gl.clearColor(0, 0, 0, 1);
   var xOffs = yOffs = 0,  drag  = 0;
   var xRot = 0;
   var yRot = 0;
   var transl = -6;
   drawScene();

  var Now = new Date();
  var lastTime = Now.getTime();

  var fireBeam = 0;

  function handleKeys() {
    xRot = 0.0;
    yRot = 0.0;

     if (g_keyDown[37] || g_keyDown[65]) { yRot = yRot + 1; }
     if (g_keyDown[39] || g_keyDown[68]) { yRot = yRot - 1; }
     if (g_keyDown[38] || g_keyDown[87]) { xRot = xRot - 1; }
     if (g_keyDown[40] || g_keyDown[83]) { xRot = xRot + 1; }
     if (xRot > 45) { xRot = 20; }
     if (xRot < -45) { xRot = -20; }
     if (yRot > 45) { yRot = 20; }
     if (yRot < -45) { yRot = -20; }
     if (g_keyDown[32]) {fireBeam = 1;} else {fireBeam = 0;}

     if (document.inv.invert.checked) {xRot = -xRot; }

     ptMatrix.rotate(xRot, 1, 0, 0);
     ptMatrix.rotate(yRot, 0, 1, 0);
     invPtMatrix.rotate(-yRot, 0, 1, 0);
     invPtMatrix.rotate(-xRot, 1, 0, 0);
  }

  function tick() {
    Now = new Date();
    var currentTime = Now.getTime();
    var diff = currentTime - lastTime;
    diff = diff / 1000.0
    if (isNaN(diff)) { diff = 0; }
    lastTime = currentTime;

    stepPos(diff);
  }

  function makeMv(zrot) {
    rotMat.makeIdentity();
    rotMat.rotate(zrot, 0, 1, 0);
    mvMatrix.makeIdentity();
    mvMatrix.multRight( rotMat );
  }


  var xPos = 20.0;
  var yPos = 20.0;
  var zPos = 20.0;

  var xVel = 0.0;
  var yVel = 0.0;
  var zVel = 0.0;

  var fScale = 1.0;
  function stepPos (diff) {
     var xA = 0.0;
     var yA = 0.0;
     var zA = 0.0;

     for (i = 0; i < nStars; i++) {
       var xD = (xsPos[i] - xPos);
       var yD = (ysPos[i] - yPos);
       var zD = (zsPos[i] - zPos);

       var magD = Math.sqrt(xD*xD + yD*yD + zD*zD);
       var magD3 = magD / (Math.pow(2, 4*pull[i]) - 0.99);

       xA = xA + fScale * xD / magD3;
       yA = yA + fScale * yD / magD3;
       zA = zA + fScale * zD / magD3;

     }


     xVel = xVel + xA * diff;
     yVel = yVel + yA * diff;
     zVel = zVel + zA * diff;


     xVel = xVel * (1 - diff);
     yVel = yVel * (1 - diff);
     zVel = zVel * (1 - diff);
      
     xPos = xPos + xVel * diff;
     yPos = yPos + yVel * diff;
     zPos = zPos + zVel * diff;

  }

  var lastBF = 0;
  var beamCutoff = 3.14159 / 1000.0;
  function beamFlip () {

    if (lastBF == 0 && (fireBeam == 1 || mbdown == 1)) {
      var lx = -ptMatrix.m31;
      var ly = -ptMatrix.m32;
      var lz = -ptMatrix.m33;

      for (i = 0; i < nStars ; i++) {
        var xsP = xsPos[i] - xPos;
        var ysP = ysPos[i] - yPos;
        var zsP = zsPos[i] - zPos;

        var rx = invPtMatrix.m11 * xsP + invPtMatrix.m21 * ysP + invPtMatrix.m31 * zsP;
        var ry = invPtMatrix.m12 * xsP + invPtMatrix.m22 * ysP + invPtMatrix.m32 * zsP;
        var rz = invPtMatrix.m13 * xsP + invPtMatrix.m23 * ysP + invPtMatrix.m33 * zsP;
        var rxz = Math.sqrt(rx * rx + rz * rz);

        var phi = Math.atan2(-rx, -rz);
        var theta = Math.atan(ry/rxz);
//        document.getElementById("status").innerHTML = " " + phi + " " + theta;
        if (Math.sqrt(phi * phi + theta*theta) < beamCutoff) {
           if (fireBeam == 1) { pull[i] = pull[i] - 0.1; }
           else  { pull [i] = pull[i] + 0.2; }

           if (pull[i] > 1.0) { pull[i] = 1.0; }
           if (pull[i] < -1.0) { pull[i] = -1.0;}
         }
 
      }
    }
   lastBF = (fireBeam == 1 || mbdown == 1);
  }

  var win = 0;
  function checkWin (){
     var winRad = 2.5;
     var xD = (xsPos[nStars - 1] - xPos);
     var yD = (ysPos[nStars - 1] - yPos);
     var zD = (zsPos[nStars - 1] - zPos);

     if (Math.sqrt(xD*xD + yD*yD + zD*zD) < winRad) {
       win = 1;
       document.getElementById("status").innerHTML = "YOU WIN!";
     }

   }



  function drawScene(){
    checkWin();
    if (win == 1) { return;}
    handleKeys();
    tick();
    updateStars();
    prMatrix.makeIdentity();
    prMatrix.perspective(25, c_w/c_h, 0.1, 1000);

    beamFlip();
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    makeMv(0.0);
    mvMatrix.translate(-xPos, -yPos, -zPos);
    mvMatrix.multRight(invPtMatrix);

    gl.useProgram(starProg);

    gl.uniformMatrix4fv( starmvMatLoc, false, new Float32Array(mvMatrix.getAsArray()) );
    gl.uniformMatrix4fv( starprMatLoc, false, new Float32Array(prMatrix.getAsArray()) );

    gl.bindBuffer(gl.ARRAY_BUFFER, starposBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, starPos, gl.STREAM_DRAW);
    gl.vertexAttribPointer(starposLocation, 3, gl.FLOAT, false, 0, 0);

    gl.bindBuffer(gl.ARRAY_BUFFER, starcolorBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, starColor, gl.STREAM_DRAW);
    gl.vertexAttribPointer(starcolorLocation, 3, gl.FLOAT, false, 0, 0);

    gl.bindBuffer(gl.ARRAY_BUFFER, staroffBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, starOffset, gl.STREAM_DRAW);
    gl.vertexAttribPointer(staroffLocation, 3, gl.FLOAT, false, 0, 0);
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, starIndexBuffer);
    gl.drawElements(gl.TRIANGLES, nStars*6, gl.UNSIGNED_SHORT, 0);




    if (fireBeam == 1 || mbdown == 1) {
    gl.useProgram(beamProg);

    makeMv(0.0);
    gl.uniformMatrix4fv( beammvMatLoc, false, new Float32Array(mvMatrix.getAsArray()) );
    gl.uniformMatrix4fv( beamprMatLoc, false, new Float32Array(prMatrix.getAsArray()) );


    gl.bindBuffer(gl.ARRAY_BUFFER, beamposBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, beamPos, gl.STREAM_DRAW);
    gl.vertexAttribPointer(beamposLocation, 3, gl.FLOAT, false, 0, 0);
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, starIndexBuffer);
    gl.drawElements(gl.POINTS, nBeam, gl.UNSIGNED_SHORT, 0);
    }




    gl.useProgram(prog);
    gl.bindBuffer(gl.ARRAY_BUFFER, posBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, enterprise_pt, gl.STREAM_DRAW);
    gl.vertexAttribPointer(posLocation, 3, gl.FLOAT, false, 0, 0);
    gl.bindBuffer(gl.ARRAY_BUFFER, normBuffer);
    gl.vertexAttribPointer(normLocation, 3, gl.FLOAT, false, 0, 0);
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);

    gl.uniformMatrix4fv( gl.getUniformLocation(prog,"prMatrix"),
      false, new Float32Array(prMatrix.getAsArray()) );
    makeMv(-90.0);
    mvMatrix.rotate(10, 1, 0, 0);
    mvMatrix.translate(0, -.1, -2);

    gl.uniformMatrix4fv( mvMatLoc, false, new Float32Array(mvMatrix.getAsArray()) );
    for(var i=0; i < mt_enterprise; i++) {
      gl.drawElements(gl.TRIANGLES, 6*n_enterprise*n_enterprise, gl.UNSIGNED_SHORT, 12*n_enterprise*n_enterprise*i);
    }

    gl.flush();
  }

  canvas.resize = function (){
    c_w = window.innerWidth - 25;  c_h = window.innerHeight - 75;
    canvas.width = c_w;   canvas.height = c_h;
  }



  var mouseX = c_w/2;
  var mouseY = c_h/2;
  var shiftDown = 0;
  canvas.onmousemove = function ( ev ) {
     var xDiff = (ev.clientX - mouseX)/15.0;
     var yDiff = (ev.clientY - mouseY)/15.0;

     if (mouseX == c_w/2 && mouseY == c_h/2) {
       xDiff = 0;
       yDiff = 0;
     }
     mouseX = ev.clientX;
     mouseY = ev.clientY;
     yRot = xDiff;
     xRot = yDiff;
     if (document.inv.invert.checked) {xRot = -xRot;} 

   
     if (ev.shiftKey) {
     ptMatrix.rotate(xRot, 1, 0, 0);
     ptMatrix.rotate(yRot, 0, 1, 0);
     invPtMatrix.rotate(yRot, 0, 1, 0);
     invPtMatrix.rotate(xRot, 1, 0, 0);
     }
  }

  var mbdown = 0;
  canvas.onmousedown = function (ev) {
    mbdown = 1;
    if ( ev.shiftKey ) { shiftDown = 1};
  }

  canvas.onmouseup = function(ev) {
    mbdown = 0;
    shiftDown = 0;
  }

/*
  canvas.onmousedown = function ( ev ){
     drag  = 1;
     xOffs = ev.clientX;  yOffs = ev.clientY;
  }
  canvas.onmouseup = function ( ev ){
     drag  = 0;
     xOffs = ev.clientX;  yOffs = ev.clientY;
  }
  canvas.onmousemove = function ( ev ){
     if ( drag == 0 ) return;
     if ( ev.shiftKey ) {
        transl *= 1 + (ev.clientY - yOffs)/1000;
        yRot = yRot - xOffs + ev.clientX; }
     else {
        yRot = yRot - xOffs + ev.clientX;  xRot = xRot - yOffs + ev.clientY; }
     xOffs = ev.clientX;   yOffs = ev.clientY;
     drawScene();
  }

  var wheelHandler = function(ev) {
    var del = 1.1;
    if (ev.shiftKey) del = 1.01;
    var ds = ((ev.detail || ev.wheelDelta) > 0) ? del : (1 / del);
    transl *= ds;
    drawScene();
    ev.preventDefault();
  };
  canvas.addEventListener('DOMMouseScroll', wheelHandler, false);
  canvas.addEventListener('mousewheel', wheelHandler, false);

*/
  getEventKeyChar = function(event) {
    if (!event) {
        event = window.event;
    }
    var charCode = 0;
    if (event.keyIdentifier)
        charCode = o3djs.event.keyIdentifierToChar(event.keyIdentifier);
    if (!charCode)
        charCode = (window.event) ? window.event.keyCode : event.charCode;
    if (!charCode)
        charCode = event.keyCode;
    return charCode;
  };

  document.onkeydown = function (event) {
    event = event || window.event;
    g_keyDown[event.keyCode] = true;
  }

  document.onkeyup = function (event) {
    event = event || window.event;
    g_keyDown[event.keyCode] = false;
  }

  setInterval(drawScene, 40);
}

</script> 
</head>
<body onload="webGLStart();" onresize="canvas.resize();"> 
   <canvas id="canvas" width="500" height="500"></canvas> 
<div id="status">MOOOOO</div>
<form name="inv">
Invert y axis: <input type="checkbox" id="invert" /><a href="http://www.ibiblio.org/e-notes/webgl/models.htm#spline">Credit for model</a>
</form>
</body></html>
